{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import triton\n",
    "import triton.language as tl\n",
    "import torch\n",
    "import math\n",
    "from copy import deepcopy\n",
    "import os\n",
    "os.environ['TRITON_PRINT_AUTOTUNING'] = '1'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# @triton.autotune([triton.Config({}, num_stages=ns, num_warps=nw)\n",
    "#                   for ns in [1,2,4]\n",
    "#                   for nw in [1, 2,4,8,16]],\n",
    "#                   key=['BLOCK_N'])\n",
    "@triton.jit\n",
    "def _short_softmax_fwd_kernel(X,\n",
    "                          Y,\n",
    "                          N,\n",
    "                          BLOCK_N:tl.constexpr,\n",
    "                          ):\n",
    "    row_id = tl.cast(tl.program_id(0), tl.int64)\n",
    "\n",
    "    X += row_id * N\n",
    "    Y += row_id * N\n",
    "\n",
    "    cols = tl.arange(0, BLOCK_N)\n",
    "    x = tl.load(X + cols, mask=cols<N, other=float('-inf')).to(tl.float32)\n",
    "    y = tl.softmax(x)\n",
    "    tl.store(Y + cols, y, mask=cols<N)\n",
    "\n",
    "# @triton.autotune([triton.Config({\"BLOCK_N\":bn}, num_stages=ns, num_warps=nw)\n",
    "#                   for ns in [1,2,4]\n",
    "#                   for nw in [4,8,16, 32]\n",
    "#                   for bn in [4096, 8192]],\n",
    "#                   key=['N'])\n",
    "@triton.jit\n",
    "def _long_softmax_fwd_kernel(X,\n",
    "                          Y,\n",
    "                          N,\n",
    "                          BLOCK_N:tl.constexpr,\n",
    "                          ):\n",
    "    row_id = tl.cast(tl.program_id(0), tl.int64)\n",
    "\n",
    "    X += row_id * N\n",
    "    Y += row_id * N\n",
    "\n",
    "    l_i = 0.\n",
    "    m_i = float('-inf')\n",
    "    for start in range(0, N, BLOCK_N):\n",
    "        cols = start + tl.arange(0, BLOCK_N)\n",
    "        x = tl.load(X + cols, mask=cols<N, other=float('-inf')).to(tl.float32)\n",
    "        m_i_new = tl.maximum(m_i, tl.max(x))\n",
    "        l_i = l_i * tl.exp(m_i - m_i_new) + tl.sum(tl.exp(x - m_i_new))\n",
    "        m_i = m_i_new\n",
    "    lse = m_i + tl.log(l_i)\n",
    "    \n",
    "    for start in range(0, N, BLOCK_N):\n",
    "        cols = start + tl.arange(0, BLOCK_N)\n",
    "        x = tl.load(X + cols, mask=cols<N, other=float('-inf')).to(tl.float32)\n",
    "        y = tl.exp(x - lse)\n",
    "        tl.store(Y + cols, y, mask=cols<N)\n",
    "\n",
    "def softmax_fwd(x):\n",
    "    M, N = x.shape\n",
    "    P = 1024 * 16\n",
    "    y = torch.empty_like(x)\n",
    "    grid = (M,)\n",
    "    if N <= P:\n",
    "        BLOCK_N = triton.next_power_of_2(N)\n",
    "        \n",
    "        kwargs = {\"num_warps\": 16, \"num_stages\":1}\n",
    "        if BLOCK_N <= 8192:\n",
    "            kwargs = {\"num_warps\": 8, \"num_stages\":2}\n",
    "        _short_softmax_fwd_kernel[grid](x, \n",
    "                                    y,\n",
    "                                    N, \n",
    "                                    BLOCK_N,\n",
    "                                    **kwargs\n",
    "                                    )\n",
    "    else:\n",
    "        # BLOCK_N = 2048\n",
    "\n",
    "        kwargs = {\"BLOCK_N\":8192, \"num_warps\": 16, \"num_stages\":1}\n",
    "        _long_softmax_fwd_kernel[grid](x, \n",
    "                                    y,\n",
    "                                    N, \n",
    "                                    # BLOCK_N,\n",
    "                                    **kwargs\n",
    "                                    )\n",
    "    return y\n",
    "\n",
    "# @triton.autotune([triton.Config({}, num_stages=ns, num_warps=nw)\n",
    "#                   for ns in [1,2,4]\n",
    "#                   for nw in [1, 2,4,8,16]],\n",
    "#                   key=['BLOCK_N'])\n",
    "@triton.jit\n",
    "def _short_softmax_bwd_kernel(Y,\n",
    "                              DY,\n",
    "                              DX,\n",
    "                              N,\n",
    "                              BLOCK_N:tl.constexpr,\n",
    "                          ):\n",
    "    row_id = tl.cast(tl.program_id(0), tl.int64)\n",
    "\n",
    "    DX += row_id * N\n",
    "    DY += row_id * N\n",
    "    Y += row_id * N\n",
    "\n",
    "    cols = tl.arange(0, BLOCK_N)\n",
    "    y = tl.load(Y + cols, mask=cols<N, other=0.).to(tl.float32)\n",
    "    dy = tl.load(DY + cols, mask=cols<N, other=0.).to(tl.float32)\n",
    "    ydy = y * dy\n",
    "    dx = ydy - y * tl.sum(ydy)\n",
    "    tl.store(DX + cols, dx, mask=cols<N)\n",
    "\n",
    "# @triton.autotune([triton.Config({\"BLOCK_N\":bn}, num_stages=ns, num_warps=nw)\n",
    "#                   for ns in [1,2,4]\n",
    "#                   for nw in [4,8,16, 32]\n",
    "#                   for bn in [4096, 8192]],\n",
    "#                   key=['N'])\n",
    "@triton.jit\n",
    "def _long_softmax_bwd_kernel(Y,\n",
    "                          DY,\n",
    "                          DX,\n",
    "                          N,\n",
    "                          BLOCK_N:tl.constexpr,\n",
    "                          ):\n",
    "    row_id = tl.cast(tl.program_id(0), tl.int64)\n",
    "\n",
    "    DX += row_id * N\n",
    "    DY += row_id * N\n",
    "    Y += row_id * N\n",
    "\n",
    "    acc = 0.\n",
    "    for start in range(0, N, BLOCK_N):\n",
    "        cols = start + tl.arange(0, BLOCK_N)\n",
    "        y = tl.load(Y + cols, mask=cols<N, other=0.).to(tl.float32)\n",
    "        dy = tl.load(DY + cols, mask=cols<N, other=0.).to(tl.float32)\n",
    "        acc += tl.sum(y * dy)\n",
    "\n",
    "    for start in range(0, N, BLOCK_N):\n",
    "        cols = start + tl.arange(0, BLOCK_N)\n",
    "        y = tl.load(Y + cols, mask=cols<N, other=0.).to(tl.float32)\n",
    "        dy = tl.load(DY + cols, mask=cols<N, other=0.).to(tl.float32)\n",
    "        dx = y * dy - y * acc\n",
    "        tl.store(DX + cols, dx, mask=cols<N)\n",
    "\n",
    "def softmax_bwd(y, dy):\n",
    "    M, N = y.shape\n",
    "    P = 1024 * 16\n",
    "    dx = torch.empty_like(y)\n",
    "    grid = (M,)\n",
    "    if N <= P:\n",
    "        BLOCK_N = triton.next_power_of_2(N)\n",
    "        \n",
    "        kwargs = {\"num_warps\": 16, \"num_stages\":1}\n",
    "        if BLOCK_N <= 8192:\n",
    "            kwargs = {\"num_warps\": 4, \"num_stages\":2}\n",
    "        _short_softmax_bwd_kernel[grid](y, \n",
    "                                    dy,\n",
    "                                    dx,\n",
    "                                    N, \n",
    "                                    BLOCK_N,\n",
    "                                    **kwargs\n",
    "                                    )\n",
    "    else:\n",
    "        # BLOCK_N = 2048\n",
    "\n",
    "        kwargs = {\"BLOCK_N\":8192, \"num_warps\": 32, \"num_stages\":4}\n",
    "        _long_softmax_bwd_kernel[grid](y, \n",
    "                                    dy,\n",
    "                                    dx,\n",
    "                                    N, \n",
    "                                    # BLOCK_N,\n",
    "                                    **kwargs\n",
    "                                    )\n",
    "    return dx\n",
    "\n",
    "class _SoftmaxFunction(torch.autograd.Function):\n",
    "\n",
    "    @staticmethod\n",
    "    def forward(ctx, x):\n",
    "        y = softmax_fwd(x)\n",
    "        ctx.y = y\n",
    "        return y\n",
    "    \n",
    "    @staticmethod\n",
    "    def backward(ctx, dy):\n",
    "        dx = softmax_bwd(ctx.y, dy)\n",
    "        return dx\n",
    "\n",
    "def triton_softmax(x:torch.Tensor, dim=-1):\n",
    "    # ndim = x.dim()\n",
    "    # target_dim = dim if dim >=0 else ndim + dim\n",
    "    # new_x = torch.movedim(x, target_dim, -1).contiguous()\n",
    "    \n",
    "    # input_shape = new_x.shape\n",
    "    # new_x = new_x.view(-1, input_shape[-1])\n",
    "\n",
    "    # p = _SoftmaxFunction.apply(new_x)\n",
    "    # p = p.view(input_shape)\n",
    "    # p = torch.movedim(p, -1, target_dim)\n",
    "    p = _SoftmaxFunction.apply(x)\n",
    "    return p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 精度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "x1 = torch.randn(1024, 1024 * 4, device='cuda', dtype=torch.bfloat16)\n",
    "x1.requires_grad_(True)\n",
    "x2 = deepcopy(x1)\n",
    "\n",
    "y1 = triton_softmax(x1)\n",
    "y2 = torch.softmax(x2, -1)\n",
    "dy = torch.randn_like(y1)\n",
    "y1.backward(dy)\n",
    "y2.backward(dy)\n",
    "print(torch.allclose(y1,y2, 1e-3, 1e-3))\n",
    "print(torch.allclose(x1.grad, x2.grad, 1e-3, 1e-3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# benchmark"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## forward"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "forward:\n",
      "          n    Triton     Torch\n",
      "0    2048.0  0.008703  0.016209\n",
      "1    4096.0  0.010350  0.019312\n",
      "2    8192.0  0.014772  0.025500\n",
      "3   16384.0  0.024959  0.041543\n",
      "4   32768.0  0.051884  0.070477\n",
      "5   65536.0  0.106554  0.140087\n",
      "6  131072.0  0.209674  0.293484\n",
      "7  262144.0  0.416173  0.557591\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['n'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[1024 * 2**i for i in range(1, 8+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['triton', 'torch'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"Triton\",\n",
    "            \"Torch\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"forward\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'b':1024},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(b, n, provider):\n",
    "    device = 'cuda'\n",
    "    dtype = torch.bfloat16\n",
    "    x = torch.randn(b, n,device=device, dtype=dtype)\n",
    "\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'torch':\n",
    "        ms = triton.testing.do_bench(lambda: torch.softmax(x, -1))\n",
    "    if provider == 'triton':\n",
    "        ms = triton.testing.do_bench(lambda: triton_softmax(x))\n",
    "    return ms\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## backward"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "forward:\n",
      "          n    Triton     Torch\n",
      "0    2048.0  0.011272  0.014951\n",
      "1    4096.0  0.012521  0.021378\n",
      "2    8192.0  0.019422  0.036196\n",
      "3   16384.0  0.034855  0.063226\n",
      "4   32768.0  0.068298  0.119613\n",
      "5   65536.0  0.163866  0.235319\n",
      "6  131072.0  0.321053  0.456465\n",
      "7  262144.0  0.634442  0.897698\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['n'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[1024 * 2**i for i in range(1, 8+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['triton', 'torch'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"Triton\",\n",
    "            \"Torch\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"forward\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'b':1024},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(b, n, provider):\n",
    "    device = 'cuda'\n",
    "    dtype = torch.bfloat16\n",
    "    x = torch.randn(b, n,device=device, dtype=dtype)\n",
    "    x.requires_grad_(True)\n",
    "\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'torch':\n",
    "        y = torch.softmax(x, -1)\n",
    "        dy = torch.randn_like(y)\n",
    "        ms = triton.testing.do_bench(lambda: y.backward(dy, retain_graph=True), grad_to_none=[x])\n",
    "    if provider == 'triton':\n",
    "        y = triton_softmax(x, -1)\n",
    "        dy = torch.randn_like(y)\n",
    "        ms = triton.testing.do_bench(lambda: y.backward(dy, retain_graph=True), grad_to_none=[x])\n",
    "    return ms\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## forward + backward"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAGwCAYAAABB4NqyAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAZwtJREFUeJzt3Xd4FFXfxvHvJqQCSagp9CYo0ksERUGCARGkV+lFpFgQKRaK+giijw9SBFQgoJTQRBEElCKCCIL0JiBShISekITUnfePedkYQQQkmU32/lzXXrJnzw6/HQh7e86ZMzbDMAxEREREXIib1QWIiIiIZDUFIBEREXE5CkAiIiLichSARERExOUoAImIiIjLUQASERERl6MAJCIiIi4nl9UFOCO73c6ZM2fImzcvNpvN6nJERETkNhiGwdWrVwkJCcHN7dZjPApAN3HmzBmKFStmdRkiIiJyF06dOkXRokVv2UcB6Cby5s0LmCfQz8/P4mpERETkdsTGxlKsWDHH9/itKADdxPVpLz8/PwUgERGRbOZ2lq9oEbSIiIi4HAUgERERcTkKQCIiIuJytAboX0hLSyMlJcXqMnIUDw8P3N3drS5DRERyOAWgu2AYBlFRUVy5csXqUnKkgIAAgoKCtAeTiIhkGgWgu3A9/BQuXBhfX199Ud8jhmGQkJDAuXPnAAgODra4IhERyakUgO5QWlqaI/wUKFDA6nJyHB8fHwDOnTtH4cKFNR0mIiKZQoug79D1NT++vr4WV5JzXT+3Wl8lIiKZRQHoLmnaK/Po3IqISGZTABIRERGXowAkIiIiLkcBSG5q9OjRVK1a1eoyREREMoUCkAuw2Wy3fIwePfqG9wwZMoS1a9c6nnfv3p0WLVpkXdEiIpIjGYbB179+jd2wW1qHLoN3AWfPnnX8OjIykpEjR3L48GFHW548eRy/NgyDtLQ08uTJk6FdRETk34pJjOHZr58lcn8k74a9y9CHh1pWi0aA7gHDgPj4rH8Yxu3VFxQU5Hj4+/tjs9kczw8dOkTevHn55ptvqFGjBl5eXmzatCnDFNjo0aOZPXs2X375pWPUaMOGDQDs3buXxx9/HB8fHwoUKEDfvn2Ji4tz/N7XR47ef/99goODKVCgAAMGDNAl7iIiLubnP36m+sfVidwfibvNHTebtRFEI0D3QEICWDFYEhcHuXPfm2MNHz6c999/n9KlS5MvXz5HwAFzOuzgwYPExsYya9YsAPLnz098fDzh4eHUqVOHn3/+mXPnztG7d28GDhxIRESE4/3r168nODiY9evXc/ToUdq3b0/VqlXp06fPvSleRESclt2w878t/2P42uGk2lMp4V+C+a3nU6dYHUvrUgASAN58800aNWp009fy5MmDj48PSUlJBAUFOdpnz55NYmIic+bMIff/J7HJkyfTrFkz3n33XQIDAwHIly8fkydPxt3dnQoVKtC0aVPWrl2rACQiksOdjz9Pt2Xd+OboNwC0vr81nzb/lADvAGsLQwHonvD1NUdjrPh975WaNWve8XsOHjxIlSpVHOEH4OGHH8Zut3P48GFHAKpYsWKGW1oEBwezd+/ef1+0iIg4rXXH1/HM0mc4G3cW71zeTAifQN8afZ1ms1sFoHvAZrt3U1FWyZ2JH8DDwyPDc5vNht1u7ep/ERHJHKn2VMZsGMN/fvgPBgb3F7yfyDaRVAqsZHVpGWgRtNwWT09P0tLSMrTdf//97N69m/j4eEfb5s2bcXNzo3z58lldooiIWOxUzCkazG7A2z+8jYFBr2q9+LnPz04XfkABSG5TyZIl2bNnD4cPH+bChQukpKTQuXNnvL296datG/v27WP9+vUMGjSILl26OKa/RETENXx56EuqTKvCppObyOuZl/mt5/Np80/J7emcUyQKQHJb+vTpQ/ny5alZsyaFChVi8+bN+Pr6snr1ai5dukStWrVo06YNDRs2ZPLkyVaXKyIiWSQxNZHnv3meFpEtuJx4mZohNdn57E46PNjB6tJuyWYYt7ubjOuIjY3F39+fmJgY/Pz8MryWmJjI8ePHKVWqFN7e3hZVmLPpHIuIZA+/XvyV9ovbsytqFwAv13mZdxq+g6e7pyX13Or7+6+0CFpERETu2Jzdc+i/oj/xKfEU9C3I7BazebLck1aXddssnQLbuHEjzZo1IyQkBJvNxrJly27Zv3v37je9l1XFihUdfUaPHn3D6xUqVMjkTyIiIuIa4pLj6PpFV7ot60Z8SjwNSjZgd7/d2Sr8gMUBKD4+nipVqjBlypTb6v/hhx9y9uxZx+PUqVPkz5+ftm3bZuhXsWLFDP02bdqUGeWLiIi4lJ1nd1J9enU+2/MZbjY33qz/Jt92+ZaQvCFWl3bHLJ0Ca9KkCU2aNLnt/v7+/vj7+zueL1u2jMuXL9OjR48M/XLlypVhx+J/kpSURFJSkuN5bGzsbb9XREQkpzMMg8nbJjPk2yEkpyVT1K8o81rNo16JelaXdtey9VVgM2bMICwsjBIlSmRoP3LkCCEhIZQuXZrOnTtz8uTJWx5n7NixjnDl7+9PsWLFMrNsERGRbONiwkVaRLbg+VXPk5yWTPPyzdn17K5sHX4gGwegM2fO8M0339C7d+8M7aGhoURERLBq1SqmTp3K8ePHqVevHlevXv3bY40YMYKYmBjH49SpU5ldvoiIiNP74cQPVJ1ela8Of4WnuycTG09kWftlFPAtYHVp/1q2vQps9uzZBAQE0KJFiwztf55Sq1y5MqGhoZQoUYKFCxfSq1evmx7Ly8sLLy+vzCxXREQk20izp/HOD+8w+vvR2A075fKXI7JNJNWCq1ld2j2TLQOQYRjMnDmTLl264Ol5670GAgICuO+++zh69GgWVSciIpJ9nbl6hmeWPsP639cD0KVyF6Y8OYW8XnktruzeypZTYN9//z1Hjx792xGdP4uLi+PYsWMEBwdnQWVy3e1sayAiIs7lmyPfUGVaFdb/vp7cHrmZ3WI2c1rOyXHhBywOQHFxcezatYtdu3YBcPz4cXbt2uVYtDxixAi6du16w/tmzJhBaGgoDz744A2vDRkyhO+//57ff/+dH3/8kZYtW+Lu7k7Hjh0z9bM4s5vtnfTnx+jRo60uUURELJSclsyQNUN4ct6TXEi4QNWgquzou4OuVW78Ds4pLJ0C2759Ow0aNHA8Hzx4MADdunUjIiKCs2fP3nAFV0xMDEuWLOHDDz+86TFPnz5Nx44duXjxIoUKFeKRRx7hp59+olChQpn3QZzc2bNnHb+OjIxk5MiRHD582NGWJ0+eOzpeSkoKHh4e96w+ERGxzrFLx+iwpAPbz2wHYFDtQYxvNB7vXDn7VkSWjgDVr18fwzBueERERAAQERHBhg0bMrzH39+fhIQE+vTpc9NjLliwgDNnzpCUlMTp06dZsGABZcqUyeRP4tyCgoIcD39/f2w2m+N54cKF+eCDDyhatCheXl5UrVqVVatWOd77+++/Y7PZiIyM5LHHHsPb25u5c+cCMHPmTCpWrIiXlxfBwcEMHDgww+974cIFWrZsia+vL+XKleOrr77K0s8tIiK3FrkvkmrTq7H9zHbyeedjWftlTGwyMceHH8imi6CdjWEYJKQkZPnv6+vhi81m+1fH+PDDD/nvf//L9OnTqVatGjNnzqR58+bs37+fcuXKOfoNHz6c//73v1SrVg1vb2+mTp3K4MGDGTduHE2aNCEmJobNmzdnOPaYMWMYP3487733HpMmTaJz586cOHGC/Pnz/6uaRUTk30lISeCFb17g052fAvBI8UeY22ouxf2LW1xZ1lEAugcSUhLIM/bOppHuhbgRceT2zP2vjvH+++8zbNgwOnToAMC7777L+vXrmTBhQoZblLz44ou0atXK8fztt9/m5Zdf5oUXXnC01apVK8Oxu3fv7lh79c477zBx4kS2bdtG48aN/1XNIiJy9/ZG76X94vYcvHAQGzZeq/cao+qPIpeba0UC1/q0kkFsbCxnzpzh4YcfztD+8MMPs3v37gxtNWvWdPz63LlznDlzhoYNG97y+JUrV3b8Onfu3Pj5+XHu3Ll7ULmIiNwpwzD4eMfHvLj6RRJTEwnKE8TcVnN5vNTjVpdmCQWge8DXw5e4EXGW/L5ZJXfu9JEmHx+f23rPXxdK22w27Hb7Pa1LRET+2ZXEK/RZ3ofFBxYD0KRsEyJaRFA4d2GLK7OOAtA9YLPZ/vVUlBX8/PwICQlh8+bNPPbYY472zZs3U7t27b99X968eSlZsiRr167NcBWfiIg4n59O/0SHxR04EXOCXG65GNdwHC/VeQk3W7bcCvCeUQByca+88gqjRo2iTJkyVK1alVmzZrFr1y7HlV5/Z/To0fTr14/ChQvTpEkTrl69yubNmxk0aFAWVS4iIrdiN+y8/+P7vLbuNVLtqZQKKMWCNguoXeTv/wfXlSgAubjnn3+emJgYXn75Zc6dO8cDDzzAV199leEKsJvp1q0biYmJ/O9//2PIkCEULFiQNm3aZFHVIiJyK9Fx0XRd1pU1x9YA0L5ie6Y/NR1/b3+LK3MeNsMwDKuLcDaxsbH4+/sTExODn59fhtcSExM5fvw4pUqVwts75++TYAWdYxGRu/fdb9/xzNJniI6PxieXDxObTKRXtV7/etuU7OBW399/pREgERGRHCAlLYVRG0YxbtM4DAwqFqpIZJtIKhauaHVpTkkBSEREJJs7ceUEHZd0ZMvpLQA8W+NZPgj/IEuvFs5uFIBERESysaUHl9Lrq15cSbyCn5cfnzb7lLYV21pdltNTABIREcmGElMTeXn1y3y0/SMAahepzYLWCyiVr5TFlWUPCkB3SWvHM4/OrYjIrR08f5AOSzqwJ3oPAEPrDuXtx9/Gw93jH94p1ykA3aHruxsnJCTc9o7IcmcSEswby/51J2kREVdnGAYRuyIY+M1AElISKORbiM9afkZ42XCrS8t2FIDukLu7OwEBAY57Wvn6/vs7sovJMAwSEhI4d+4cAQEBuLu7W12SiIjTuJp0lX4r+jFv7zwAGpZqyGctPyM4b7DFlWVPCkB3ISgoCEA39swkAQEBjnMsIiKw48wOOizpwNFLR3G3ufNWg7cY+vBQ3N30P4p3SwHoLthsNoKDgylcuDApKSlWl5OjeHh4aORHROT/GYbBh1s/ZOi3Q0mxp1DcvzjzW8+nbrG6VpeW7SkA/Qvu7u76shYRkUxxIeECPb7swde/fg1Aywot+bT5p+T3yW9xZTmDApCIiIiT+f737+m0tBNnrp7By92LD8I/4Lmaz2nN6T2kACQiIuIk0uxpvLXxLd7a+BZ2w075AuWJbBNJlaAqVpeW4ygAiYiIOIHTsafpvLQzG09sBKB71e5MbjKZ3J65La4sZ1IAEhERsdjXv35N92XduXjtInk88zCt6TQ6V+5sdVk5mgKQiIiIRZJSkxj+3XAmbJ0AQPXg6ixovYByBcpZW5gLUAASERGxwJGLR+iwpAO/nP0FgBdDX2Rc2Di8cnlZXJlrUAASERHJYnP3zKXfin7EJcdRwKcAs56eRbPyzawuy6UoAImIiGSR+OR4Bn4zkIhdEQA8WuJR5raaS1G/otYW5oIUgERERLLA7qjdtF/cnsMXD+Nmc+ONR9/gjUff0O0sLKIAJCIikokMw2Dq9qkMXj2YpLQkQvKGMLfVXOqXrG91aS5NAUhERCSTXL52mV5f9eKLQ18A0LRcUyJaRFDQt6DFlYkCkIiISCb48dSPdFzSkZMxJ/Fw82B8o/G8EPqCbmfhJBSARERE7qE0exrvbn6XketHkmakUSZfGSLbRFIjpIbVpcmfKACJiIjcI1FxUTyz9BnWHl8LQKdKnZjadCp+Xn4WVyZ/pQAkIiJyD6w+upquy7pyLv4cvh6+TG4yme5Vu2vKy0kpAImIiPwLKWkpvL7udcb/OB6AyoGVWdB6AfcXut/iyuRWFIBERETu0vHLx+m4pCNb/9gKQP+a/Xn/iffx8fCxuDL5JwpAIiIid2HR/kX0Xt6b2KRYArwDmNF8Bq3ub2V1WXKb3Kz8zTdu3EizZs0ICQnBZrOxbNmyW/bfsGEDNpvthkdUVFSGflOmTKFkyZJ4e3sTGhrKtm3bMvFTiIiIK7mWco1+X/ej3eJ2xCbFUqdoHXY9u0vhJ5uxNADFx8dTpUoVpkyZckfvO3z4MGfPnnU8Chcu7HgtMjKSwYMHM2rUKH755ReqVKlCeHg4586du9fli4iIi9l/bj+1PqnF9B3TsWFjxCMj+L7795QIKGF1aXKHLJ0Ca9KkCU2aNLnj9xUuXJiAgICbvvbBBx/Qp08fevToAcC0adNYsWIFM2fOZPjw4Td9T1JSEklJSY7nsbGxd1yTiIjkXIZhMGPnDJ7/5nmupV4jMHcgn7X8jEZlGlldmtwlS0eA7lbVqlUJDg6mUaNGbN682dGenJzMjh07CAsLc7S5ubkRFhbGli1b/vZ4Y8eOxd/f3/EoVqxYptYvIiLZR0xiDB2XdKTP8j5cS73GE2WeYHe/3Qo/2Vy2CkDBwcFMmzaNJUuWsGTJEooVK0b9+vX55ZdfALhw4QJpaWkEBgZmeF9gYOAN64T+bMSIEcTExDgep06dytTPISIi2cO2P7ZRbXo1IvdHksstF++Gvcs3nb8hME/gP79ZnFq2ugqsfPnylC9f3vG8bt26HDt2jP/973989tlnd31cLy8vvLy87kWJIiKSA9gNO//b8j+Grx1Oqj2VkgElmd96Pg8Vfcjq0uQeyVYB6GZq167Npk2bAChYsCDu7u5ER0dn6BMdHU1QUJAV5YmISDZzLv4c3Zd155uj3wDQ5oE2fNLsEwK8A6wtTO6pbDUFdjO7du0iODgYAE9PT2rUqMHatWsdr9vtdtauXUudOnWsKlFERLKJdcfXUXVaVb45+g3eubyZ1nQaC9ssVPjJgSwdAYqLi+Po0aOO58ePH2fXrl3kz5+f4sWLM2LECP744w/mzJkDwIQJEyhVqhQVK1YkMTGRTz/9lHXr1rFmzRrHMQYPHky3bt2oWbMmtWvXZsKECcTHxzuuChMREfmrVHsqYzaM4T8//AcDg/sL3k9km0gqBVayujTJJJYGoO3bt9OgQQPH88GDBwPQrVs3IiIiOHv2LCdPnnS8npyczMsvv8wff/yBr68vlStX5rvvvstwjPbt23P+/HlGjhxJVFQUVatWZdWqVTcsjBYREQE4GXOSzks7s+mkuZyid7XeTGg8gdyeuS2uTDKTzTAMw+oinE1sbCz+/v7ExMTg5+dndTkiIpJJlh1aRs8ve3I58TJ5PfPycbOP6fBgB6vLkrt0J9/f2X4RtIiIyJ1KTE1k6LdDmbRtEgA1Q2qyoPUCyuQvY3FlklUUgERExKUcvnCYDks6sCtqFwAv13mZdxq+g6e7p7WFSZZSABIREZcxZ/cc+q/oT3xKPAV9CzK7xWyeLPek1WWJBRSAREQkx4tLjqP/iv58tsfcNLdByQZ83upzQvKGWFyZWEUBSEREcrSdZ3fSfnF7jlw6gpvNjTH1xzDikRG4u7lbXZpYSAFIRERyJMMwmLRtEq98+wrJackU9SvKvFbzqFeintWliRNQABIRkRznYsJFen7Vk68OfwVA8/LNmdl8JgV8C1hcmTgLBSAREclRfjjxA52WduJ07Gk83T15v9H7DKw9EJvNZnVp4kQUgEREJEdIs6fxzg/vMPr70dgNO+XylyOyTSTVgqtZXZo4IQUgERHJ9s5cPcMzS59h/e/rAehSuQtTnpxCXq+8FlcmzkoBSEREsrWVR1bSbVk3LiRcILdHbj5q+hFdq3S1uixxcgpAIiKSLSWnJfPq2lf575b/AlA1qCqRbSK5r8B9Flcm2YECkIiIZDvHLh2jw5IObD+zHYBBtQcxvtF4vHN5W1yZZBcKQCIikq0s2LeAvsv7cjX5Kvm88zHr6Vk8XeFpq8uSbEYBSEREsoX45HheWPUCM3bOAOCR4o8wr9U8ivkXs7gyyY4UgERExOntjd5L+8XtOXjhIDZsvFbvNUbVH0UuN32Nyd3R3xwREXFahmHw8Y6PeXH1iySmJhKcJ5jPW33O46Uet7o0yeYUgERExCldSbxCn+V9WHxgMQBNyjYhokUEhXMXtrgyyQkUgERExOn8dPonOizuwImYE+Ryy8W4huN4qc5LuNncrC5NcggFIBERcRp2w857m9/j9fWvk2pPpXS+0sxvPZ/aRWpbXZrkMApAIiLiFKLjoum6rCtrjq0BoH3F9kx/ajr+3v4WVyY5kQKQiIhY7rvfvuOZpc8QHR+NTy4fJjaZSK9qvXQHd8k0CkAiImKZlLQURm0YxbhN4zAwqFioIpFtIqlYuKLVpUkOpwAkIiKWOHHlBB2XdGTL6S0APFvjWf4X/j98PHwsrkxcgQKQiIhkuaUHl9Lrq15cSbyCn5cfnzb7lLYV21pdlrgQBSAREcky11Ku8fKal5m6fSoAoUVCmd96PqXylbK4MnE1CkAiIpIlDp4/SIclHdgTvQeAYQ8P460Gb+Hh7mFxZeKKFIBERCRTGYZBxK4IBn4zkISUBAr5FuKzlp8RXjbc6tLEhSkAiYhIpolNiuW5Fc8xb+88ABqWashnLT8jOG+wxZWJq1MAEhGRTLH9zHY6LO7AscvHcLe581aDtxj2yDDdzkKcggKQiIjcU4Zh8OHWDxn67VBS7CkU9y/O/NbzqVusrtWliTgoAImIyD1zIeECPb7swde/fg1AywotmdF8Bvl88llcmUhGCkAiInJPfP/793Ra2okzV8/g5e7FB+Ef8FzN53Q7C3FKCkAiIvKvpNnTeGvjW7y18S3shp3yBcoT2SaSKkFVrC5N5G8pAImIyF07HXuazks7s/HERgB6VO3BpCaTyO2Z2+LKRG5NAUhERO7K8sPL6f5ldy5du0QezzxMazqNzpU7W12WyG2x9FrEjRs30qxZM0JCQrDZbCxbtuyW/ZcuXUqjRo0oVKgQfn5+1KlTh9WrV2foM3r0aGw2W4ZHhQoVMvFTiIi4lqTUJF5a9RLNFzTn0rVLVA+uzi99f1H4kWzF0gAUHx9PlSpVmDJlym3137hxI40aNWLlypXs2LGDBg0a0KxZM3bu3JmhX8WKFTl79qzjsWnTpswoX0TE5Ry5eIS6M+syYesEAF4MfZEfe/5IuQLlrC1M5A5ZOgXWpEkTmjRpctv9J0yYkOH5O++8w5dffsny5cupVq2aoz1XrlwEBQXd9nGTkpJISkpyPI+Njb3t94qIuIq5e+bSb0U/4pLjKOBTgIgWETx131NWlyVyV7L1dpx2u52rV6+SP3/+DO1HjhwhJCSE0qVL07lzZ06ePHnL44wdOxZ/f3/Ho1ixYplZtohIthKfHE+PL3vwzBfPEJccx6MlHmVXv10KP5KtZesA9P777xMXF0e7du0cbaGhoURERLBq1SqmTp3K8ePHqVevHlevXv3b44wYMYKYmBjH49SpU1lRvoiI09sdtZsaH9cgYlcEbjY3Rj82mnVd11HUr6jVpYn8K9n2KrB58+YxZswYvvzySwoXLuxo//OUWuXKlQkNDaVEiRIsXLiQXr163fRYXl5eeHl5ZXrNIiLZhWEYfPTzR7y85mWS0pIIyRvCvFbzeKzkY1aXJnJPZMsAtGDBAnr37s2iRYsICwu7Zd+AgADuu+8+jh49mkXViYhkb5evXabXV7344tAXADQt15SIFhEU9C1ocWUi9062mwKbP38+PXr0YP78+TRt2vQf+8fFxXHs2DGCg4OzoDoRkext88nNVJ1elS8OfYGHmwf/C/8fyzsuV/iRHMfSEaC4uLgMIzPHjx9n165d5M+fn+LFizNixAj++OMP5syZA5jTXt26dePDDz8kNDSUqKgoAHx8fPD39wdgyJAhNGvWjBIlSnDmzBlGjRqFu7s7HTt2zPoPKCKSTaTZ03h387uMXD+SNCONMvnKENkmkhohNawuTSRTWDoCtH37dqpVq+a4hH3w4MFUq1aNkSNHAnD27NkMV3B9/PHHpKamMmDAAIKDgx2PF154wdHn9OnTdOzYkfLly9OuXTsKFCjATz/9RKFChbL2w4mIZBNRcVGEfx7Oa+teI81Io1OlTvzy7C8KP5Kj2QzDMKwuwtnExsbi7+9PTEwMfn5+VpcjIpJpVh9dTddlXTkXfw5fD1+mPDmFblW66Q7uki3dyfd3tlwELSIi/05KWgqvr3ud8T+OB6ByYGUi20RSoaBuHSSuQQFIRMTFHL98nI5LOrL1j60A9K/Zn/efeB8fDx+LKxPJOgpAIiIuZNH+RfRe3pvYpFgCvAOY0XwGre5vZXVZIllOAUhExAUkpCTw0qqX+PiXjwGoU7QO81vPp0RACYsrE7GGApCISA63/9x+2i9uz/7z+7FhY/gjwxlTfwwe7h5WlyZiGQUgEZEcyjAMZuycwfPfPM+11GsE5g7k81afE1b61jvoi7gCBSARkRwoJjGGZ79+lsj9kQA8UeYJ5rSYQ2CeQIsrE3EOCkAiIjnMtj+20WFxB45fOU4ut1z85/H/MKTuENxs2e7uRyKZRgFIRCSHsBt2PtjyASPWjiDVnkrJgJLMbz2fh4o+ZHVpIk5HAUhEJAc4F3+Obsu6seroKgDaPNCGT5p9QoB3gLWFiTgpBSARkWxu3fF1PLP0Gc7GncU7lzcTwifQt0Zf3c5C5BYUgEREsqlUeypjNozhPz/8BwOD+wveT2SbSCoFVrK6NBGnpwAkIpINnYw5Saclndh8ajMAvav15sMmH+Lr4WtxZSLZgwKQiEg2s+zQMnp+2ZPLiZfJ65mXj5t9TIcHO1hdlki2ogAkIpJNJKYm8sqaV5j882QAaoXUYn7r+ZTJX8biykSyHwUgEZFs4PCFw3RY0oFdUbsAGFJnCP9p+B883T2tLUwkm1IAEhFxcnN2z6H/iv7Ep8RT0Lcgs1vM5slyT1pdlki2pgAkIuKkriZdZcDKAXy25zMAGpRswOetPickb4jFlYlkfwpAIiJOaOfZnbRf3J4jl47gZnNjTP0xjHhkBO5u7laXJpIjKACJiDgRwzCYtG0Sr3z7CslpyRT1K8q8VvOoV6Ke1aWJ5CgKQCIiTuJiwkV6ftWTrw5/BcDT5Z9mRvMZFPAtYHFlIjmPApCIiBP44cQPdFraidOxp/F09+T9Ru8zsPZA3c5CJJMoAImIWCjNnsY7P7zD6O9HYzfs3FfgPha0XkC14GpWlyaSoykAiYhY5MzVM3Re2pkNv28AoGuVrkx5cgp5PPNYW5iIC1AAEhGxwMojK+m2rBsXEi6Q2yM3HzX9iK5VulpdlojLUAASEclCyWnJvLr2Vf675b8AVA2qSmSbSO4rcJ/FlYm4FgUgEZEscuzSMTos6cD2M9sBeL7284xvNB6vXF4WVybiehSARESywIJ9C+i7vC9Xk6+Szzsfs56exdMVnra6LBGXpQAkIpKJ4pPjeWHVC8zYOQOAR4o/wrxW8yjmX8ziykSsExUF+fKBl4WDn27W/dYiIjnb3ui91PqkFjN2zsCGjTcefYP13dYr/IjL+vlneOYZKF4cIiOtrUUjQCIi95hhGEzfMZ2XVr9EYmoiwXmCmdtqLg1KNbC6NJEsl5wMS5bAxInw00/p7Zs2QVcLL3xUABIRuYeuJF6hz/I+LD6wGIAmZZswu8VsCuUuZHFlIlkrOho+/himToWzZ802T0/o0AEGDYKaNa2tTwFIROQe+en0T3RY3IETMSfwcPNgXNg4XnzoRdxsWm0grmPHDnO0Z8ECc/QHICgI+veHvn0hMNDa+q5TABIR+Zfshp33Nr/Ha+teI81Io3S+0ixovYBaRWpZXZpIlkhJgaVLzeDz44/p7aGh8MIL0Lq1OfrjTBSARET+hei4aLou68qaY2sAaF+xPdOfmo6/t7/FlYlkvvPnzWmujz6CM2fMNg8PaNfOnOYKDbW2vltRABIRuUvfHvuWLl90ITo+Gp9cPkxqMome1XrqDu6S4+3caY72zJ8PSUlmW2Ag9OsHzz4LwcHW1nc7LJ2Y3rhxI82aNSMkJASbzcayZcv+8T0bNmygevXqeHl5UbZsWSIiIm7oM2XKFEqWLIm3tzehoaFs27bt3hcvIi4rJS2FEd+NIPzzcKLjo3mw8INs77udXtV7KfxIjpWaCosXQ716UL06RESY4admTfjsMzhxAkaPzh7hBywOQPHx8VSpUoUpU6bcVv/jx4/TtGlTGjRowK5du3jxxRfp3bs3q1evdvSJjIxk8ODBjBo1il9++YUqVaoQHh7OuXPnMutjiIgLOXHlBI9FPMa4zeMwMOhXox/bem/jgUIPWF2aSKa4cAHGjYNSpaBtW/Py9Vy5oGNH2LIFtm0z9/axclPDu2EzDMOwuggAm83GF198QYsWLf62z7Bhw1ixYgX79u1ztHXo0IErV66watUqAEJDQ6lVqxaTJ08GwG63U6xYMQYNGsTw4cNvetykpCSSro/hAbGxsRQrVoyYmBj8/PzuwacTkZxgyYEl9F7emyuJV/D38ueTZp/QtmJbq8sSyRS7d8OkSTB3LiQmmm2FCpnTXP36QUiItfXdTGxsLP7+/rf1/Z2trs3csmULYWFhGdrCw8PZsmULAMnJyezYsSNDHzc3N8LCwhx9bmbs2LH4+/s7HsWKaZdWEUl3LeUa/Vf0p82iNlxJvEJokVB2PrtT4UdynNRU82qu+vWhalWYMcMMP9Wrw+zZcPIkvPmmc4afO5WtFkFHRUUR+JcNBAIDA4mNjeXatWtcvnyZtLS0m/Y5dOjQ3x53xIgRDB482PH8+giQiMjB8wfpsKQDe6L3ADDs4WG81eAtPNw9LK5M5N65dAk+/RSmTDFDDoC7O7RpA88/D3XqQE5b3patAlBm8fLywiu7TV6KSKYyDIOIXREM/GYgCSkJFM5dmM9afsYTZZ6wujSRe2bvXnOa6/PP4do1s61AAfNKrueeg6JFra0vM2WrABQUFER0dHSGtujoaPz8/PDx8cHd3R13d/eb9gkKCsrKUkUkG4tNiuW5Fc8xb+88AMJKh/FZy88IyqN/RyT7S0uD5cvNy9jXr09vr1LF3LSwQwfw8bGuvqySrdYA1alTh7Vr12Zo+/bbb6lTpw4Anp6e1KhRI0Mfu93O2rVrHX1ERG5l+5ntVJ9enXl75+Fuc+edx99h9TOrFX4k27t8Gd5/H8qWhZYtzfBzfZpr40Zzb58ePVwj/IDFI0BxcXEcPXrU8fz48ePs2rWL/PnzU7x4cUaMGMEff/zBnDlzAOjXrx+TJ09m6NCh9OzZk3Xr1rFw4UJWrFjhOMbgwYPp1q0bNWvWpHbt2kyYMIH4+Hh69OiR5Z9PRLIPwzCY8NMEhn03jBR7CsX9izO/9XzqFqtrdWki/8qBA+Zoz2efQUKC2ZY/v3lfrueeg+LFra3PKpYGoO3bt9OgQQPH8+sLkbt160ZERARnz57l5PXVWECpUqVYsWIFL730Eh9++CFFixbl008/JTw83NGnffv2nD9/npEjRxIVFUXVqlVZtWrVDQujRUSuu5Bwge7LurPiiPk/Uy0rtGRG8xnk88lncWUidyctDVauNIPPd9+lt1eubC5q7tTJdUZ6/o7T7APkTO5kHwERyd6+//17Oi3txJmrZ/By9+J/4f+jX81+2tFZsqUrV2DWLJg8GX77zWxzc4MWLczg8+ijOe9qrj/L9H2AZs+enWHaaejQoQQEBFC3bl1OnDhxN4cUEclSqfZURm8YzeNzHufM1TNUKFiBbX228Vyt5xR+JNs5dAgGDDCv2ho82Aw/+fLB0KHmr5csgccey9nh507dVQB655138Pn/sbMtW7YwZcoUxo8fT8GCBXnppZfuaYEiIvfa6djTNJzTkDHfj8Fu2OlRtQfb+2yncmBlq0sTuW12O6xYAeHhcP/95h3Z4+OhYkWYPh1On4Z334USJayu1Dnd1RqgU6dOUbZsWQCWLVtG69at6du3Lw8//DD169e/l/WJiNxTyw8vp/uX3bl07RJ5PPMwrek0OlfubHVZIrctNjZ9muv6dUQ2GzRvbk5zNWigkZ7bcVcBKE+ePFy8eJHixYuzZs0ax+Jlb29vrl3fSUlExIkkpSYx7LthfLj1QwCqB1cnsk0kZfOXtbgykdtz+LAZeiIiIC7ObPP3h969oX9/KF3a0vKynbsKQI0aNaJ3795Uq1aNX3/9lSeffBKA/fv3U0JjbSLiZH6/8jttF7Vl+5ntALz00EuMbTgWr1zaAV6cm90Oa9bAhx/C/9/zGzCnvJ5/3rwLe5481tWXnd1VAJoyZQqvv/46p06dYsmSJRQoUACAHTt20KlTp3taoIjIv7HyyEqeWfoMlxMvk98nP7NbzOap+56yuiyRW7p61bz56KRJ8OuvZpvNBk89ZQafhg01zfVv3fVl8ImJiezZs4dz585ht9szvNa8efN7UpxVdBm8SPaXZk9j9IbRvP3D2wDULlKbRW0XUdzfRXd9k2zh6FFzmmvmTDMEAfj5Qa9e5lVeZcpYW5+zu5Pv77saAVq1ahVdu3bl4sWL/DU/2Ww20tLS7uawIiL3xPn483Ra2onvfjN3gOtfsz8fhH+gKS9xSoYB335rblq4cqX5HKB8eXO0p0sXyJvX2hpzoru6DH7QoEG0bduWM2fOYLfbMzwUfkTESltObaH6x9X57rfv8PXwZW6ruUxpOkXhR5xOXJx56foDD5iXsq9YYYafJ5801/scOGAublb4yRx3NQIUHR3N4MGDdXsJEXEahmEwadskXl7zMqn2VMoXKM+SdkuoWLii1aWJZPDbb+nTXDExZlvevOaNSAcOhHLlrK3PVdxVAGrTpg0bNmygjCYjRcQJXE26Su/lvVm4fyEA7Sq249Nmn5LXS//rLM7BMGDtWnNR8/Ll6dNc5crBoEHQrZu51keyzl0tgk5ISKBt27YUKlSISpUq4eHhkeH1559//p4VaAUtghbJPg6cP0Drha05dOEQudxy8X6j93k+9HndzkKcQnw8fP65ub7nwIH09saNzfU94eHmvbrk3sj0RdDz589nzZo1eHt7s2HDhgz/0NhstmwfgEQke5i/dz59lvchPiWeInmLsLDtQuoWq2t1WSL8/jtMmQKffmreoBTM/Xq6dzenucqXt7A4Ae4yAL322muMGTOG4cOH46boKiJZLCk1iZfXvMyUn6cA0LBUQ+a1nkfh3IUtrkxcmWHAhg3maM9XX5mbGIJ56fqgQWb48fe3skL5s7sKQMnJybRv317hR0Sy3MmYk7Rd1JZtf2wD4LV6rzGm/hjc3dwtrkxcVUICzJ1rBp99+9LbGzUyp7maNAF3/fV0OncVgLp160ZkZCSvvvrqva5HRORvrTm2hk5LOnHx2kXyeefjs5af0fS+plaXJS7qxAnzMvZPPoHLl802X19zQfPAgebl7eK87ioApaWlMX78eFavXk3lypVvWAT9wQcf3JPiREQA7Iadt75/izHfj8HAoHpwdRa3XUypfKWsLk1cjGHAxo3maM+yZenTXKVKmaGnZ08ICLCyQrlddxWA9u7dS7Vq1QDY9+fxPtCVFyJyT11IuMAzS59h9bHVADxb41kmNJ6Ady5viysTV3LtGsyfbwaf3bvT2xs2NKe5mjbVNFd2c1cBaP369fe6DhGRG2z7YxttF7XlZMxJfHL5MO2paXSt0tXqssSFnDoFU6fCxx/DxYtmm48PdO1qjvg8+KC19cndu6sAJCKSmQzDYOr2qby46kVS7CmUy1+Oxe0WUzmwstWliQswDNi82RztWboUrt/hqUSJ9Gmu/PmtrVH+PQUgEXEq8cnx9P26L/P2zgOg1f2tmNl8Jv7eun5YMldiIixYYAafnTvT2+vXhxdegGbNNM2VkygAiYjTOHThEK0XtubA+QO429x5N+xdBtcZrLWFkqn++MOc5po+HS5cMNu8veGZZ8z9eypr4DFHUgASEaewaP8ien7Vk7jkOILzBBPZJpJ6JepZXZbkUIYBW7aYoz1LlkBqqtlerBgMGAC9e0OBAtbWKJlLAUhELJWclszQb4fy4dYPAahfsj7zW88nKE+QxZVJTpSUBJGRZvDZsSO9/dFHzau5nn4acumb0SXoj1lELHM69jTtFrVjy+ktAAx7eBhvP/42udz0T5PcW2fPwrRp5uPcObPNyws6dzanuapWtbQ8sYD+lRERS6z9bS0dl3TkfMJ5/L38mdNyDs3LN7e6LMlhtm41R3sWLkyf5ipSxJzm6tMHCha0tj6xjgKQiGQpu2Fn7A9jGblhJHbDTtWgqixuu5gy+ctYXZrkEMnJsGiRGXy2bUtvf+QRc5qrRQv4yw0MxAUpAIlIlrl07RJdv+jKiiMrAOhVrReTmkzCx8PH4sokJ4iKMq/kmjbN/DWApyd06mROc1Wvbm194lwUgEQkS+w4s4M2i9rw+5Xf8c7lzZQnp9CzWk+ry5Ic4OefzdGeyEhISTHbQkKgf39zmqtwYWvrE+ekACQimcowDD755RMGfTOI5LRkSucrzeK2i6kWXM3q0iQbS042L1+fOBF++im9vU4dc5qrdWtNc8mtKQCJSKZJSEnguRXPMWf3HACal2/O7BazCfAOsLYwybaio837ck2dal7ZBWbQ6dDBnOaqVcva+iT7UAASkUxx5OIRWi9szd5ze3GzufHO4+/wysOv4GZzs7o0yYZ27DBHexYsMEd/AIKC4LnnoG9f89cid0IBSETuuaUHl9Ljyx7EJsUSmDuQBW0WUL9kfavLkmwmJQW++MIMPps3p7eHhprTXG3amIucRe6GApCI3DMpaSmMWDuC/275LwD1itdjQZsFhOQNsbgyyU7On4dPPoGPPjLv0wXmNFe7duY0V2iotfVJzqAAJCL3xJmrZ2i/uD2bTm4CYEidIbzT8B083LUSVW7Prl3maM+8eeYtK8C8guu55+DZZyE42NLyJIdRABKRf23D7xvosLgD0fHR5PXMS0SLCFrd38rqsiQbSE2FZcvM4PPDD+ntNWrACy+Yoz5eXpaVJzmYU6xGnDJlCiVLlsTb25vQ0FC2/Xnrzr+oX78+NpvthkfTpk0dfbp3737D640bN86KjyLiUgzD4N1N79JwTkOi46OpVLgSO/ruUPiRf3TxIowbB6VLQ9u2ZvjJlcu8muvHH829fbp0UfiRzGP5CFBkZCSDBw9m2rRphIaGMmHCBMLDwzl8+DCFb7J71dKlS0m+fgkAcPHiRapUqULbtm0z9GvcuDGzZs1yPPfST5HIPXUl8QrdlnXjq8NfAdC1SlemNp2Kr4evxZWJM9uzxxztmTsXEhPNtkKFzCmufv3M+3SJZAXLA9AHH3xAnz596NGjBwDTpk1jxYoVzJw5k+HDh9/QP3/+/BmeL1iwAF9f3xsCkJeXF0G3eV1kUlISSdcnnIHY2Ng7/RgiLmVX1C5aL2zNb5d/w9Pdk0lNJtGneh9sNpvVpYkTSk2F5cvhww/h++/T26tVM6e52rcHb2/r6hPXZOkUWHJyMjt27CAsLMzR5ubmRlhYGFu2bLmtY8yYMYMOHTqQO3fuDO0bNmygcOHClC9fnueee46LFy/+7THGjh2Lv7+/41GsWLG7+0AiLmDmzpnUmVGH3y7/RsmAkvzY80f61uir8CM3uHQJ3nsPypaFVq3M8OPubq7r2bTJ3NunWzeFH7GGpSNAFy5cIC0tjcDAwAztgYGBHDp06B/fv23bNvbt28eMGTMytDdu3JhWrVpRqlQpjh07xquvvkqTJk3YsmUL7u7uNxxnxIgRDB482PE8NjZWIUjkL66lXGPgyoHM3DUTgKblmjKn5Rzy++T/h3eKq9m3DyZNgs8+g2vXzLYCBcxprueeg6JFra1PBJxgCuzfmDFjBpUqVaJ27doZ2jt06OD4daVKlahcuTJlypRhw4YNNGzY8IbjeHl5aY2QyC0cu3SMNovasCtqF242N95q8BbDHxmuXZ3FIS0Nvv7aXN+zbl16e5Uq5jRXhw7g42NdfSJ/ZWkAKliwIO7u7kRHR2doj46O/sf1O/Hx8SxYsIA333zzH3+f0qVLU7BgQY4ePXrTACQif+/LQ1/SbVk3YpJiKORbiPmt59OwtH6OxHT5MsycCZMnw++/m21ubtCypblbc716oNlRcUaW/u+bp6cnNWrUYO3atY42u93O2rVrqVOnzi3fu2jRIpKSknjmmWf+8fc5ffo0Fy9eJFi7aInctlR7KsO/G06LyBbEJMVQt1hdfnn2F4UfAeDAgfTprCFDzPCTPz8MGwbHj8PixfDoowo/4rwsnwIbPHgw3bp1o2bNmtSuXZsJEyYQHx/vuCqsa9euFClShLFjx2Z434wZM2jRogUFChTI0B4XF8eYMWNo3bo1QUFBHDt2jKFDh1K2bFnCw8Oz7HOJZGdRcVF0XNKRDb9vAODF0BcZ32i8dnV2cWlpsHKlOc313Xfp7ZUqmaM9nTqBr3ZBkGzC8gDUvn17zp8/z8iRI4mKiqJq1aqsWrXKsTD65MmTuLllHKg6fPgwmzZtYs2aNTccz93dnT179jB79myuXLlCSEgITzzxBG+99ZbW+Yjchh9O/ED7xe05G3eWPJ55mNF8Bu0qtrO6LLFQTAzMmmUubP7tN7PNzQ2eftoMPo89ppEeyX5shmEYVhfhbGJjY/H39ycmJgY/Pz+ryxHJEoZh8MGWDxj23TDSjDQeKPQAS9otoULBClaXJhY5dMhc2xMRAfHxZltAAPTpA/37Q8mSFhYnchN38v1t+QiQiFgvJjGGnl/1ZOnBpQB0qtSJ6U9NJ49nHosrk6xmt8OqVeY01+rV6e0VK5qjPZ07w1+2XRPJlhSARFzcnug9tF7YmqOXjuLh5sGHjT+kX81+2tjQxcTGmiM9kybB0aNmm80GzZvDoEHw+OOa5pKcRQFIxIXN2T2Hfl/341rqNYr7F2dR20XULlL7n98oOcavv5rTXLNmQVyc2ebvD716wYAB5s1KRXIiBSARF5SYmsgL37zAx798DEDjso35vOXnFPAt8A/vlJzAboc1a8xprm++SW+vUMGc5urSBfJo9lNyOAUgERdz/PJx2ixqwy9nf8GGjdH1R/P6o69rV2cXcPUqzJljTnMdPmy22WzQtKkZfMLCNM0lrkMBSMSFrPh1BV2+6MLlxMsU8CnA3FZzCS+r/bFyuqNHYcoUc8fm2Fizzc8PevY0p7nKlrW2PhErKACJuIA0exqjNoziPz/8B4DaRWqzqO0iivsXt7gyySyGYW5WOHEirFhhPge47z5ztKdrV8ib19oaRaykACSSw52LP0enJZ1Ye9y85cyAWgP47xP/xSuXNgbNieLizLuwT5oEBw+mtzdpYt6UtFEjcxNDEVenACSSg/146kfaLWrHH1f/wNfDl0+bfUrHSh2tLksywW+/mdNcM2aYOzeDuZC5Rw8YONAc+RGRdApAIjmQYRhM3DqRId8OIdWeSoWCFVjSbgkPFHrA6tLkHjIMWLfOnOZavjx9mqtsWXPvnu7dzbU+InIjBSCRHOZq0lV6L+/Nwv0LAWhfsT2fNPuEvF5a8JFTxMfD55+b01z796e3h4eb63saN9Y0l8g/UQASyUH2n9tP64WtOXzxMLnccvHBEx8wsPZA7eqcQ/z+O3z0EXz6KVy+bLblzm2O9AwcaO7jIyK3RwFIJIeYu2cufb/uS0JKAkXyFmFR20XUKVbH6rLkXzIM+P57c5rryy/NTQzB3KF50CBzjY+/v7U1imRHCkAi2VxSahKDVw/mo+0fARBWOox5reZRKHchiyuTfyMhAebNM4PP3r3p7Y0amdNcTZqAu7t19YlkdwpAItnYiSsnaLe4Hdv+2AbA6/VeZ3T90bi76Zsxuzp50pzm+uQTuHTJbPP1NfftGTQIHtA6dpF7QgFIJJtadXQVnZd25tK1S+TzzsfnrT7nyXJPWl2W3AXDgB9+MEd7vvgifZqrZElzbU/PnpAvn6UliuQ4CkAi2UyaPY23Nr7Fm9+/iYFBjeAaLG63mJIBJa0uTe7QtWswf74ZfHbvTm9//HFzmuuppzTNJZJZFIBEspELCRfovLQza46tAaBfjX78r/H/8M7lbXFlcidOnYKpU+Hjj+HiRbPNx8e8C/ugQfDgg9bWJ+IKFIBEsomtp7fSdlFbTsWewieXD9Ofmk6XKl2sLktuk2HA5s3maM/SpZCWZrYXL25Oc/XqBfnzW1ujiCtRABJxcoZh8NHPH/HS6pdIsadQLn85lrRbQqXASlaXJrchMREiI83g88sv6e3165vTXM2aQS79SyyS5fRjJ+LE4pLj6Lu8L/P3zQeg1f2tmPX0LPy8dH8DZ/fHHzBtGkyfDufPm23e3vDMM+Y0V+XK1tYn4uoUgESc1MHzB2mzqA0Hzh/A3ebO+Ebjeemhl7SrsxMzDPjpJ3O0Z/FiSE0124sVgwEDoHdvKFDA2hpFxKQAJOKEIvdF0uurXsSnxBOcJ5iFbRfySPFHrC5L/kZSEixcaAaf7dvT2+vVgxdegKef1jSXiLPRj6SIE0lOS+aVNa8wcdtEABqUbMD81vMJzBNocWVyM2fPmtNc06bBuXNmm5cXdOpkTnNVq2ZtfSLy9xSARJzE6djTtFvUji2ntwAw4pERvNngTXK56cfU2Wzdao72LFoEKSlmW5Ei0L8/9OkDhXQXEhGnp39ZRZzAd799R8clHbmQcAF/L38+a/kZzco3s7os+ZPkZHNdz4cfwrZt6e0PP2xezdWyJXh4WFefiNwZBSARC9kNO+/88A4j14/EwKBaUDUWt1tM6XylrS5N/l90tHkl19SpEBVltnl6QseO5jRXjRrW1icid0cBSMQil65dossXXVh5ZCUAvar1YlKTSfh4+FhcmYC5mHniRFiwIH2aKzjYnObq2xcKF7a2PhH5dxSARCyw/cx22ixsw4mYE3jn8uajJz+iR7UeVpfl8lJSYMkSM/hs2ZLeXqeOOc3VqpU5+iMi2Z8CkEgWMgyDj3d8zPOrnic5LZky+cqwuN1iqgZVtbo0l3bunHlfrqlT4cwZs83DA9q3N4NPrVrW1ici954CkEgWSUhJoN/X/fhsz2cAPF3+aSJaRBDgHWBtYS7sl1/M0Z75881FzgCBgfDcc/DssxAUZG19IpJ5FIBEssCvF3+l9cLW7Du3D3ebO+80fIdX6r6iXZ0tkJICX3xhBp/Nm9Pba9UyNy1s21bTXCKuQAFIJJMtObCEHl/24GryVQJzBxLZJpLHSj5mdVku58IF+OQT+OgjOH3abMuVC9q1M6e5QkOtrU9EspYCkEgmSUlLYfh3w/ngpw8AqFe8HpFtIgnOG2xxZa5l1y6YNAnmzjVvWQHmFVz9+pnTXCEhlpYnIhZRABLJBGeunqH94vZsOrkJgCF1hvBOw3fwcNdOeVkhNRW+/NKc5tq4Mb29Rg1zmqtdO/OWFSLiutysLgBgypQplCxZEm9vb0JDQ9n2521W/yIiIgKbzZbh4e3tnaGPYRiMHDmS4OBgfHx8CAsL48iRI5n9MUQAWH98PdWmV2PTyU34efmxtN1S3nviPYWfLHDxIrz7LpQuDW3amOEnVy7o0MFc7/Pzz9Cli8KPiDhBAIqMjGTw4MGMGjWKX375hSpVqhAeHs6563cWvAk/Pz/Onj3reJw4cSLD6+PHj2fixIlMmzaNrVu3kjt3bsLDw0lMTMzsjyMuzG7YGbdpHGGfhXEu/hyVAyuzvc92Wt7f0urScrw9e8x7cBUtCsOHw6lTULAgvPYa/P67eZVX3bqgNeci4mBYrHbt2saAAQMcz9PS0oyQkBBj7NixN+0/a9Ysw9/f/2+PZ7fbjaCgIOO9995ztF25csXw8vIy5s+ff1s1xcTEGIARExNzex9CXN6lhEtGs3nNDEZjMBqj2xfdjPjkeKvLytFSUw1j6VLDqF/fMCD9Ua2aYcyaZRjXrlldoYhktTv5/rZ0BCg5OZkdO3YQFhbmaHNzcyMsLIwtf96G9S/i4uIoUaIExYoV4+mnn2b//v2O144fP05UVFSGY/r7+xMaGvq3x0xKSiI2NjbDQ+R27Ty7kxof12D5r8vxcvfi46c+ZtbTs/D18LW6tBzp0iV47z0oU8bcmXnDBnB3Ny9f/+EH2LEDuneHv8yMi4hkYOki6AsXLpCWlkZgYGCG9sDAQA4dOnTT95QvX56ZM2dSuXJlYmJieP/996lbty779++naNGiRP3/3Qpvdszrr/3V2LFjGTNmzD34ROJqZvwygwErB5CUlkTJgJIsabeE6sHVrS4rR9q3z7ya67PP4No1s61AAfO+XM89B8WKWVufiGQv2e4qsDp16lCnTh3H87p163L//fczffp03nrrrbs65ogRIxg8eLDjeWxsLMX0r6ncwrWUawxYOYBZu2YB8NR9TzGnxRzy+eSzuLKcJS0Nvv7avJpr3br09ipVzL17OnYEH907VkTugqUBqGDBgri7uxMdHZ2hPTo6mqDb3IPew8ODatWqcfToUQDH+6KjowkOTt9vJTo6mqpVq970GF5eXnjpshC5TUcvHaXNwjbsjt6Nm82Ntxq8xfBHhuNms/yaghzjyhWYMQMmTzYXMQO4uUHLlmbwqVdPC5pF5N+x9F9sT09PatSowdq1ax1tdrudtWvXZhjluZW0tDT27t3rCDulSpUiKCgowzFjY2PZunXrbR9T5O8sO7SMGh/XYHf0bgr5FmLNM2t4td6rCj/3yIED0L8/FCkCQ4aY4Sd/fhg2DH77DRYvhkcfVfgRkX/P8imwwYMH061bN2rWrEnt2rWZMGEC8fHx9OjRA4CuXbtSpEgRxo4dC8Cbb77JQw89RNmyZbly5QrvvfceJ06coHfv3gDYbDZefPFF3n77bcqVK0epUqV44403CAkJoUWLFlZ9TMnmUu2pvLb2Ncb/OB6AusXqsrDNQor4FbG4suzPboeVK81prm+/TW+vVMkc7enUCXy1nlxE7jHLA1D79u05f/48I0eOJCoqiqpVq7Jq1SrHIuaTJ0/i5pb+f9eXL1+mT58+REVFkS9fPmrUqMGPP/7IAw884OgzdOhQ4uPj6du3L1euXOGRRx5h1apVN2yYKHI7ouKi6LC4A9+f+B6Alx56iXfD3tXGhv9STAzMmmVOcx07Zra5ucHTT5vB57HHNNIjIpnHZhiGYXURziY2NhZ/f39iYmLw8/Ozuhyx0Pe/f0+HJR2Iiosij2ceZjafSduKba0uK1s7dMgMPREREB9vtgUEmBsZ9u8PJUtaWJyIZGt38v1t+QiQiDMyDIP3fnyPV9e+SpqRRsVCFVnSbgnlC5a3urRsyW6HVavMaa7Vq9PbK1Y0R3s6d4bcua2rT0RcjwKQyF9cSbxCt2Xd+OrwVwB0qdyFqU2nkttT39B3KjbWHOmZNAn+/0JNbDZo3hwGDYLHH9c0l4hYQwFI5E92nt1Jm0Vt+O3yb3i6ezKx8UT61uiLTd/Sd+TXX81prlmzIC7ObPP3h169YMAA82alIiJWUgASwZzymrlzZoZdnRe3XUyNkBpWl5Zt2O2wZo05zfXNN+ntFSqY01xdukCePNbVJyLyZwpA4vISUhIYsHIAEbsiAGharilzWs4hv09+awvLJq5ehdmzzWmuX38122w2aNrUDD5hYZrmEhHnowAkLu3IxSO0WdSGPdF7cLO58XaDtxn2yDBtbHgbjh5Nn+a6fv9gPz/o2dOc5ipb1tr6RERuRQFIXNbSg0vp8WUPYpNiKZy7MAtaL6BBqQZWl+XUDAO++w4+/NDcvPD6Jhrly5uLmrt2hbx5ra1RROR2KACJy0lJS2H4d8P54KcPAHik+CNEtokkJG+IxZU5r7g48y7skybBwYPp7U8+aU5zNWpkbmIoIpJdKACJSzlz9QztF7dn08lNAAypM4R3Gr6jXZ3/xm+/wZQp5o1JY2LMtrx5oUcPc5rrvvusrU9E5G4pAInLWHd8HR2XdORc/Dn8vPyIeDqClve3tLosp2MYsG6deTXX8uXp01zlypnTXN26mWt9RESyMwUgyfHshp1xm8bxxvo3sBt2KgdWZnHbxZQrUM7q0pxKfDx8/rkZfA4cSG9v3Nic5goP1zSXiOQcCkCSo126domuX3RlxZEVAPSo2oMpT07Bx8PH4sqcx++/m9Ncn34KV66YbXnyQPfuMHCgucBZRCSnUQCSHGv7me20WdiGEzEn8M7lzZQnp9CzWk+ry3IKhgEbNpijPV99ZW5iCFCmjDnN1b27uXOziEhOpQAkOY5hGEzfMZ0XVr1AcloypfOVZkm7JVQNqmp1aZZLSIC5c83gs29fenujRuY0V5Mm4O5uXX0iIllFAUhylPjkePqt6Mfnez4H4OnyTxPRIoIA7wBrC7PYiRPw0UfwySdw+bLZ5utrLmgeOBAeeMDa+kREspoCkOQYhy8cpvXC1uw/vx93mztjG45lSN0hLnsjU8OAjRvN0Z5ly9KnuUqVMkNPz54QEGBlhSIi1lEAkhxh4f6F9PqqF3HJcQTlCSKyTSSPlnjU6rIsce0azJ9vBp/du9PbGzY0p7maNtU0l4iIApBka8lpybyy5hUmbpsIQP2S9Znfej5BeYIsrizrnToFU6fCxx/DxYtmm4+PeXuKgQPhwQetrU9ExJkoAEm2dSrmFO0Wt+On0z8BMPzh4bz1+FvkcnOdv9aGAZs3m6M9S5dCWprZXqJE+jRXft3UXkTkBq7zTSE5yrfHvqXT0k5cSLhAgHcAc1rMoVn5ZlaXlWUSE2HBAjP47NyZ3t6ggTnN1ayZprlERG5FAUiyFbth5+2NbzN6w2gMDKoHV2dR20WUzlfa6tKyxB9/mNNc06fDhQtmm7c3dOli7t9TqZK19YmIZBcKQJJtXEi4wDNLn2H1sdUA9Kneh4lNJuKdy9viyjKXYcCWLeZoz5IlkJpqthcrZk5z9eoFBQpYW6OISHajACTZwtbTW2m7qC2nYk/hk8uHqU2n0q1qN6vLylRJSRAZaQafHTvS2x97zJzmat4ccuknWETkruifT3FqhmEw5ecpDF49mBR7CuXyl2NJuyVUCsy5cz1nzsC0aeY017lzZpuXF3TubAafKlWsrU9EJCdQABKnFZccR5/lfViwbwEAre9vzcynZ+Ln5WdxZZnjp5/M0Z5Fi9KnuYoWhf79oU8fKFjQ2vpERHISBSBxSgfOH6D1wtYcunCIXG65eK/Re7wQ+kKO29U5KckMPBMnws8/p7c/8og52tOiBXh4WFaeiEiOpQAkTmfe3nn0Wd6HhJQEQvKGsLDNQh4u/rDVZd1TUVHmNNe0aRAdbbZ5ekKnTubVXNWrW1ufiEhOpwAkTiMpNYnBqwfz0faPAGhYqiHzWs+jcO7CFld27/z8M3z4ISxcCCkpZltISPo0V+Gc81FFRJyaApA4hRNXTtB2UVt+PmPOA71e73VG1x+Nu1v2380vOdm8fH3iRHOdz3V165rTXK1aaZpLRCSrKQCJ5VYeWckzS5/hcuJl8vvk5/OWn9OkXBOry/rXoqPN+3JNnQpnz5ptnp7QoYM5zVWzprX1iYi4MgUgsUyaPY3RG0bz9g9vA1ArpBaL2i6iREAJiyv7d3bsMEd7FiwwR38AgoLMaa6+fSEw0Nr6REREAUgsci7+HJ2WdGLt8bUA9K/Znw/CP8Arl5fFld2dlBTzZqQTJ8KPP6a3P/SQOc3VurU5+iMiIs5BAUiy3I+nfqTdonb8cfUPfD18+aTZJ3Sq1Mnqsu7K+fPmNNdHH5kbGIK5nqd9e3Oaq3Zta+sTEZGbUwCSLGMYBh9u/ZBXvn2FVHsqFQpWYEm7JTxQ6AGrS7tjO3eaoz3z55t7+YA5tfXcc/Dss+aUl4iIOC8FIMkSsUmx9PqqF4sPLAagfcX2fNLsE/J65bW4stuXmgpffGEGn02b0ttr1YIXXoA2bcxbVoiIiPNTAJJMtzd6L60XtubIpSN4uHnwQfgHDKg1INvs6hwVBbNmmdNcp0+bbblyQdu25vqehx6ytj4REblzblYXADBlyhRKliyJt7c3oaGhbNu27W/7fvLJJ9SrV498+fKRL18+wsLCbujfvXt3bDZbhkfjxo0z+2PITczZPYfQT0M5cukIxfyK8UOPHxhYe6DTh5+UFPjyS3j6afN+XK++aoafQoXgjTfgxAmYN0/hR0Qku7J8BCgyMpLBgwczbdo0QkNDmTBhAuHh4Rw+fJjCN9kWd8OGDXTs2JG6devi7e3Nu+++yxNPPMH+/fspUqSIo1/jxo2ZNWuW47mX5iayVGJqIs9/8zyf/PIJAOFlwvm81ecU9HXuO3oeOgQzZ8KcOem3qACoU8dc29Ohg6a5RERyApthGIaVBYSGhlKrVi0mT54MgN1up1ixYgwaNIjhw4f/4/vT0tLIly8fkydPpmvXroA5AnTlyhWWLVt2VzXFxsbi7+9PTEwMfn45887jmen45eO0WdSGX87+gg0bo+uP5rV6rzntrs5Xr5q3ppg5M+Ml7IULQ7du0KMH3H+/dfWJiMjtuZPvb0tHgJKTk9mxYwcjRoxwtLm5uREWFsaWLVtu6xgJCQmkpKSQP3/+DO0bNmygcOHC5MuXj8cff5y3336bAgUK3PQYSUlJJF2/lAfzBMrdWX54OV2XdeVK4hUK+BRgXut5PFHmCavLuoFhwObNZuhZuBDi4812d3d48kno1cv8r25RISKSM1kagC5cuEBaWhqBf9kaNzAwkEOHDt3WMYYNG0ZISAhhYWGOtsaNG9OqVStKlSrFsWPHePXVV2nSpAlbtmzB3f3GUYixY8cyZsyYf/dhXFyqPZU31r3BuM3jAHio6EMsbLOQYv7FLK4so7NnzemtmTPh11/T2++7D3r2hK5dITjYuvpERCRrWL4G6N8YN24cCxYsYMOGDXh7ezvaO3To4Ph1pUqVqFy5MmXKlGHDhg00bNjwhuOMGDGCwYMHO57HxsZSrJhzfXE7s6i4KDou6ciG3zcA8ELoC4xvNB5Pd+fY+jglBVauhBkzzP+mpZntuXNDu3Zm8Hn4YXDyddkiInIPWRqAChYsiLu7O9F/Xm0KREdHE/QPO8m9//77jBs3ju+++47KlSvfsm/p0qUpWLAgR48evWkA8vLy0iLpu/TtsW/puqwrUXFR5PHMw4zmM2hXsZ3VZQFw8GD6guZz59Lb69Y1Q0+7dpA3+2xDJCIi95ClAcjT05MaNWqwdu1aWrRoAZiLoNeuXcvAgQP/9n3jx4/nP//5D6tXr6bmbdxS+/Tp01y8eJFgzW3cM4mpiYz4bgQTtk4AoGKhiixut5gKBStYWtf1Bc0zZsCfl5FpQbOIiPyZ5VNggwcPplu3btSsWZPatWszYcIE4uPj6dGjBwBdu3alSJEijB07FoB3332XkSNHMm/ePEqWLElUVBQAefLkIU+ePMTFxTFmzBhat25NUFAQx44dY+jQoZQtW5bw8HDLPmdOsjd6L52Xdmbvub0ADKg1gPGNxuPr4WtJPbda0Ny0qTnaowXNIiLyZ5YHoPbt23P+/HlGjhxJVFQUVatWZdWqVY6F0SdPnsTNLX2/xqlTp5KcnEybNm0yHGfUqFGMHj0ad3d39uzZw+zZs7ly5QohISE88cQTvPXWW5rm+pfshp1JWycx7LthJKUlUTh3YWY2n0nT+5paUs+tFjT36gVdumhBs4iI3Jzl+wA5I+0DdKOzV8/S/cvurDm2BoCm5Zoyo/kMAvME/sM7762UFFixwgw9f13Q3L69OdpTt64WNIuIuKJssw+QZA/LDi2j91e9uXjtIj65fPjvE/+lX81+WXo7i1staO7Vy7wvlxY0i4jI7VIAkr8VnxzPS6tfctzOolpQNea2msv9hbJmFfHVqxAZaQafPy9oDgw09+vp2RMqWLvmWkREsikFILmpn//4mc5LO3Pk0hFs2Hil7iu89fhbmb63j2HApk3pC5oTEsx2LWgWEZF7SQFIMkizpzFu0zhGfz+aVHsqRf2KMqfFHBqUapCpv+/ZszB7thl8jhxJby9fPn2H5n/YGkpEROS2KQCJw+9XfqfLF13YdHITAO0qtmNa02nk88mXKb9fSgp8/bUZer75RguaRUQk6ygACQBz98yl/8r+xCbFktczL5OfnEyXyl0yZaHzgQNm6Pnss4wLmh9+OH2H5jx57vlvKyIi4qAA5OKuJF6h/4r+zN83H4C6xeryecvPKZWv1D39fWJj0xc0//RTentgYPoOzVrQLCIiWUUByIVtPLGRLl904WTMSdxt7ox8bCSv1nuVXG735q/F9QXNM2bAokUZFzQ/9ZQ52tOkiRY0i4hI1lMAckHJacmM3jCacZvGYWBQJl8ZPm/1OQ8VfeieHP/MmfQFzUePpreXL5++Q7MWNIuIiJUUgFzM4QuH6by0MzvO7gCgZ9WeTGg8gbxe/24XweRkc4fmGTPMBc12u9meOzd06GCO9tSpowXNIiLiHBSAXIRhGHy842MGrxlMQkoC+bzz8UmzT2j9QOu7Pub58+YU1/r1sGCB+fy6hx9O36FZC5pFRMTZKAC5gPPx5+m9vDdfHf4KgIalGjK7xWyK+BW57WMYBvz+O/zwg/nYtAkOHcrY5/qC5p49zekuERERZ6UAlMOtOrqKHl/2ICouCk93T8Y2HMuLD72Im83tlu+z22HfvoyB548/buxXsSLUq2cuZtaCZhERyS4UgHKoaynXGPbdMCZtmwTAA4UeYF6reVQJqnLT/klJsH17euDZvBliYjL2yZULatY0A0+9euYmhQUKZPYnERERufcUgHKg3VG76by0M/vP7wdgUO1BvBv2Lj4ePo4+MTHmDUavB55t28wQ9Gd58pgLl68Hntq1wdc3Kz+JiIhI5lAAykEMw2DKz1N4ec3LJKclE5g7kIgWETQu25izZ+HrTemBZ8+e9Cu1ritc2Aw6jzxi/rdKFXPUR0REJKfR11sOEZ8cz7NfP8vcvXMBeLxIc5rZPyXyP4UY+AMcO3bje8qUyRh4ypXTZeoiIuIaFICyuWvXYPXPRxi0qRWnU/ZhM9zJvfk91n33IutITzM2mzmic3066+GHISTEwsJFREQspACUTaSmmqM4e/eaV2dd/+8Rty8xWnQF71iIC8RYtJC4E4/i5WWu2bkeeOrUAX9/qz+FiIiIc1AAclInT5o3D70edA4c+MsiZVsaNBgJj74DgF/Mw7ROW0id10KoVAmqVQMvL2tqFxERcXYKQE5o3z5o0AAuXMjY7utr7rtTtvIFdpTsyK9p3wHwfOgLvN/oPTzctQmPiIjI7VAAcjIHD0LDhmb4efBBaNcOKlUyH6VKwfaz22izsA2nYk/h6+HLp80+pWOljlaXLSIikq0oADmRX3+Fxx+Hc+egalVYuxby5zdfMwyDT375hEHfDCI5LZly+cuxtP1SHiz8oKU1i4iIZEcKQE7i2DEz/ERFmaM9336bHn6upVxjwMoBzNo1C4AWFVoQ8XQE/t5a1SwiInI3FICcwIkTZvj54w944AH47jsoWNB87fjl47Re2JqdUTtxs7nxzuPvMPThodi0YY+IiMhdUwCy2KlT5oLnkyfhvvvMaa/Chc3XvjnyDZ2XduZy4mUK+hZkQesFNCzd0NqCRUREcgAFIAudOWOO/Bw/bu7KvG4dBAVBYmoib37/JuM2jcPAoHaR2ixuu5hi/sWsLllERCRHUACyQFISfPIJ/Oc/5pqfUqVg/XooUgR+OPEDfZb34fDFwwD0q9GPCY0n4JVLm/qIiIjcKwpAWSglBWbPhjffNKe+wLz/1po14Fcohn5fD2P6jukABOUJYnKTybR+oLWFFYuIiORMblYX4Er69oU+fczwExICU6eamx7uSlzGAx894Ag/far34eCAgwo/IiIimUQjQFmoXz9YuRJGjIBnn4UrqWfptGwQSw4uAaBc/nJ80uwTHiv5mMWVioiI5GwKQFkoNNS82sstVwqTt01m1IZRXE2+Si63XAytO5Q3HnsD71zeVpcpIiKS4ykAZbEtZzcwcOVA9p/fD0DtIrX5+KmPqRJUxeLKREREXIcCUBYa+u1Q3vvxPQAK+BRgXNg4elbriZtNS7FERESykr55s1C94vVws7nRv2Z/fh30K72r91b4ERERsYBGgLJQs/LNODzwMGXzl7W6FBEREZfmFMMPU6ZMoWTJknh7exMaGsq2bdtu2X/RokVUqFABb29vKlWqxMqVKzO8bhgGI0eOJDg4GB8fH8LCwjhy5EhmfoTbpvAjIiJiPcsDUGRkJIMHD2bUqFH88ssvVKlShfDwcM6dO3fT/j/++CMdO3akV69e7Ny5kxYtWtCiRQv27dvn6DN+/HgmTpzItGnT2Lp1K7lz5yY8PJzExMSs+lgiIiLixGyGYRhWFhAaGkqtWrWYPHkyAHa7nWLFijFo0CCGDx9+Q//27dsTHx/P119/7Wh76KGHqFq1KtOmTcMwDEJCQnj55ZcZMmQIADExMQQGBhIREUGHDh3+sabY2Fj8/f2JiYnBz8/vHn1SERERyUx38v1t6QhQcnIyO3bsICwszNHm5uZGWFgYW7Zsuel7tmzZkqE/QHh4uKP/8ePHiYqKytDH39+f0NDQvz1mUlISsbGxGR4iIiKSc1kagC5cuEBaWhqBgYEZ2gMDA4mKirrpe6Kiom7Z//p/7+SYY8eOxd/f3/EoVkx3XRcREcnJLF8D5AxGjBhBTEyM43Hq+p1KRUREJEeyNAAVLFgQd3d3oqOjM7RHR0cTFBR00/cEBQXdsv/1/97JMb28vPDz88vwEBERkZzL0gDk6elJjRo1WLt2raPNbrezdu1a6tSpc9P31KlTJ0N/gG+//dbRv1SpUgQFBWXoExsby9atW//2mCIiIuJaLN8IcfDgwXTr1o2aNWtSu3ZtJkyYQHx8PD169ACga9euFClShLFjxwLwwgsv8Nhjj/Hf//6Xpk2bsmDBArZv387HH38MgM1m48UXX+Ttt9+mXLlylCpVijfeeIOQkBBatGhh1ccUERERJ2J5AGrfvj3nz59n5MiRREVFUbVqVVatWuVYxHzy5Enc3NIHqurWrcu8efN4/fXXefXVVylXrhzLli3jwQcfdPQZOnQo8fHx9O3blytXrvDII4+watUqvL11p3URERFxgn2AnJH2ARIREcl+ss0+QCIiIiJWUAASERERl6MAJCIiIi7H8kXQzuj6sijdEkNERCT7uP69fTvLmxWAbuLq1asAuiWGiIhINnT16lX8/f1v2UdXgd2E3W7nzJkz5M2bF5vN9q+OFRsbS7FixTh16pSuKLvHdG4zh85r5tG5zTw6t5kju51XwzC4evUqISEhGbbQuRmNAN2Em5sbRYsWvafH1C02Mo/ObebQec08OreZR+c2c2Sn8/pPIz/XaRG0iIiIuBwFIBEREXE5CkCZzMvLi1GjRuHl5WV1KTmOzm3m0HnNPDq3mUfnNnPk5POqRdAiIiLicjQCJCIiIi5HAUhERERcjgKQiIiIuBwFIBEREXE5CkCZbMqUKZQsWRJvb29CQ0PZtm2b1SVZZvTo0dhstgyPChUqOF5PTExkwIABFChQgDx58tC6dWuio6MzHOPkyZM0bdoUX19fChcuzCuvvEJqamqGPhs2bKB69ep4eXlRtmxZIiIibqglu/+5bNy4kWbNmhESEoLNZmPZsmUZXjcMg5EjRxIcHIyPjw9hYWEcOXIkQ59Lly7RuXNn/Pz8CAgIoFevXsTFxWXos2fPHurVq4e3tzfFihVj/PjxN9SyaNEiKlSogLe3N5UqVWLlypV3XIuz+Kfz2r179xv+Djdu3DhDH53XG40dO5ZatWqRN29eChcuTIsWLTh8+HCGPs708387tTiL2zm39evXv+Hvbb9+/TL0cclza0imWbBggeHp6WnMnDnT2L9/v9GnTx8jICDAiI6Otro0S4waNcqoWLGicfbsWcfj/Pnzjtf79etnFCtWzFi7dq2xfft246GHHjLq1q3reD01NdV48MEHjbCwMGPnzp3GypUrjYIFCxojRoxw9Pntt98MX19fY/DgwcaBAweMSZMmGe7u7saqVascfXLCn8vKlSuN1157zVi6dKkBGF988UWG18eNG2f4+/sby5YtM3bv3m00b97cKFWqlHHt2jVHn8aNGxtVqlQxfvrpJ+OHH34wypYta3Ts2NHxekxMjBEYGGh07tzZ2LdvnzF//nzDx8fHmD59uqPP5s2bDXd3d2P8+PHGgQMHjNdff93w8PAw9u7de0e1OIt/Oq/dunUzGjdunOHv8KVLlzL00Xm9UXh4uDFr1ixj3759xq5du4wnn3zSKF68uBEXF+fo40w///9UizO5nXP72GOPGX369Mnw9zYmJsbxuqueWwWgTFS7dm1jwIABjudpaWlGSEiIMXbsWAurss6oUaOMKlWq3PS1K1euGB4eHsaiRYscbQcPHjQAY8uWLYZhmF9Obm5uRlRUlKPP1KlTDT8/PyMpKckwDMMYOnSoUbFixQzHbt++vREeHu54ntP+XP76RW23242goCDjvffec7RduXLF8PLyMubPn28YhmEcOHDAAIyff/7Z0eebb74xbDab8ccffxiGYRgfffSRkS9fPse5NQzDGDZsmFG+fHnH83bt2hlNmzbNUE9oaKjx7LPP3nYtzurvAtDTTz/9t+/Reb09586dMwDj+++/NwzDuX7+b6cWZ/bXc2sYZgB64YUX/vY9rnpuNQWWSZKTk9mxYwdhYWGONjc3N8LCwtiyZYuFlVnryJEjhISEULp0aTp37szJkycB2LFjBykpKRnOV4UKFShevLjjfG3ZsoVKlSoRGBjo6BMeHk5sbCz79+939PnzMa73uX4MV/hzOX78OFFRURk+o7+/P6GhoRnOZUBAADVr1nT0CQsLw83Nja1btzr6PProo3h6ejr6hIeHc/jwYS5fvuzoc6vzfTu1ZDcbNmygcOHClC9fnueee46LFy86XtN5vT0xMTEA5M+fH3Cun//bqcWZ/fXcXjd37lwKFizIgw8+yIgRI0hISHC85qrnVjdDzSQXLlwgLS0tw18ogMDAQA4dOmRRVdYKDQ0lIiKC8uXLc/bsWcaMGUO9evXYt28fUVFReHp6EhAQkOE9gYGBREVFARAVFXXT83n9tVv1iY2N5dq1a1y+fDnH/7lcPxc3+4x/Pk+FCxfO8HquXLnInz9/hj6lSpW64RjXX8uXL9/fnu8/H+OfaslOGjduTKtWrShVqhTHjh3j1VdfpUmTJmzZsgV3d3ed19tgt9t58cUXefjhh3nwwQcBnOrn/3ZqcVY3O7cAnTp1okSJEoSEhLBnzx6GDRvG4cOHWbp0KeC651YBSLJMkyZNHL+uXLkyoaGhlChRgoULF+Lj42NhZSK3p0OHDo5fV6pUicqVK1OmTBk2bNhAw4YNLaws+xgwYAD79u1j06ZNVpeS4/zdue3bt6/j15UqVSI4OJiGDRty7NgxypQpk9VlOg1NgWWSggUL4u7ufsPq9ujoaIKCgiyqyrkEBARw3333cfToUYKCgkhOTubKlSsZ+vz5fAUFBd30fF5/7VZ9/Pz88PHxcYk/l+uf41afMSgoiHPnzmV4PTU1lUuXLt2T8/3n1/+pluysdOnSFCxYkKNHjwI6r/9k4MCBfP3116xfv56iRYs62p3p5/92anFGf3dubyY0NBQgw99bVzy3CkCZxNPTkxo1arB27VpHm91uZ+3atdSpU8fCypxHXFwcx44dIzg4mBo1auDh4ZHhfB0+fJiTJ086zledOnXYu3dvhi+Yb7/9Fj8/Px544AFHnz8f43qf68dwhT+XUqVKERQUlOEzxsbGsnXr1gzn8sqVK+zYscPRZ926ddjtdsc/jnXq1GHjxo2kpKQ4+nz77beUL1+efPnyOfrc6nzfTi3Z2enTp7l48SLBwcGAzuvfMQyDgQMH8sUXX7Bu3bobpgCd6ef/dmpxJv90bm9m165dABn+3rrkuc3yZdcuZMGCBYaXl5cRERFhHDhwwOjbt68REBCQYaW9K3n55ZeNDRs2GMePHzc2b95shIWFGQULFjTOnTtnGIZ5eWTx4sWNdevWGdu3bzfq1Klj1KlTx/H+65dqPvHEE8auXbuMVatWGYUKFbrppZqvvPKKcfDgQWPKlCk3vVQzu/+5XL161di5c6exc+dOAzA++OADY+fOncaJEycMwzAvkQ4ICDC+/PJLY8+ePcbTTz9908vgq1WrZmzdutXYtGmTUa5cuQyXa1+5csUIDAw0unTpYuzbt89YsGCB4evre8Pl2rly5TLef/994+DBg8aoUaNuern2P9XiLG51Xq9evWoMGTLE2LJli3H8+HHju+++M6pXr26UK1fOSExMdBxD5/VGzz33nOHv729s2LAhw6XYCQkJjj7O9PP/T7U4k386t0ePHjXefPNNY/v27cbx48eNL7/80ihdurTx6KOPOo7hqudWASiTTZo0yShevLjh6elp1K5d2/jpp5+sLsky7du3N4KDgw1PT0+jSJEiRvv27Y2jR486Xr927ZrRv39/I1++fIavr6/RsmVL4+zZsxmO8fvvvxtNmjQxfHx8jIIFCxovv/yykZKSkqHP+vXrjapVqxqenp5G6dKljVmzZt1QS3b/c1m/fr0B3PDo1q2bYRjmZdJvvPGGERgYaHh5eRkNGzY0Dh8+nOEYFy9eNDp27GjkyZPH8PPzM3r06GFcvXo1Q5/du3cbjzzyiOHl5WUUKVLEGDdu3A21LFy40LjvvvsMT09Po2LFisaKFSsyvH47tTiLW53XhIQE44knnjAKFSpkeHh4GCVKlDD69OlzQ3DWeb3Rzc4pkOFn05l+/m+nFmfxT+f25MmTxqOPPmrkz5/f8PLyMsqWLWu88sorGfYBMgzXPLc2wzCMrBtvEhEREbGe1gCJiIiIy1EAEhEREZejACQiIiIuRwFIREREXI4CkIiIiLgcBSARERFxOQpAIiIi4nIUgERERMTlKACJiIiIy1EAEhEREZejACQiIiIuJ5fVBYiIZIX69etTuXJlvL29+fTTT/H09KRfv36MHj3a6tJExAIaARIRlzF79mxy587N1q1bGT9+PG+++Sbffvut1WWJiAV0N3gRcQn169cnLS2NH374wdFWu3ZtHn/8ccaNG2dhZSJiBY0AiYjLqFy5cobnwcHBnDt3zqJqRMRKCkAi4jI8PDwyPLfZbNjtdouqERErKQCJiIiIy1EAEhEREZejACQiIiIuR1eBiYiIiMvRCJCIiIi4HAUgERERcTkKQCIiIuJyFIBERETE5SgAiYiIiMtRABIRERGXowAkIiIiLkcBSERERFyOApCIiIi4HAUgERERcTkKQCIiIuJy/g8WbXKhZPEJRQAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "forward:\n",
      "          n    Triton     Torch\n",
      "0    2048.0  0.054842  0.032614\n",
      "1    4096.0  0.064595  0.043876\n",
      "2    8192.0  0.103255  0.068055\n",
      "3   16384.0  0.116412  0.127051\n",
      "4   32768.0  0.152264  0.234086\n",
      "5   65536.0  0.268516  0.466311\n",
      "6  131072.0  0.528474  0.932238\n",
      "7  262144.0  1.048490  1.821155\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['n'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[1024 * 2**i for i in range(1, 8+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['triton', 'torch'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"Triton\",\n",
    "            \"Torch\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"forward\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'b':1024},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(b, n, provider):\n",
    "    device = 'cuda'\n",
    "    dtype = torch.bfloat16\n",
    "    x = torch.randn(b, n,device=device, dtype=dtype)\n",
    "    x.requires_grad_(True)\n",
    "    dy = torch.randn_like(x)\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'torch':\n",
    "        ms = triton.testing.do_bench(lambda: torch.softmax(x, -1).backward(dy))\n",
    "    if provider == 'triton':\n",
    "        ms = triton.testing.do_bench(lambda: triton_softmax(x).backward(dy), grad_to_none=[x])\n",
    "    return ms\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
